\documentclass{report}

\usepackage[utf8]{inputenc}
\usepackage[pdftex,bookmarks=true]{hyperref}
\usepackage{listings}
\usepackage{graphicx}
\usepackage{subfig}
\usepackage{float}
\usepackage[top=3cm, bottom=2.5cm, left=3cm, right=3cm]{geometry}

\renewcommand{\abstractname}{Sumário}
\renewcommand{\contentsname}{Conteúdo}
\renewcommand{\listtablename}{Lista de tabelas}
\renewcommand{\listfigurename}{Lista de figuras}
\renewcommand{\tablename}{Tabela}
\renewcommand{\figurename}{Imagem}
\renewcommand{\bibname}{Bibliografia}

\setcounter{tocdepth}{3}
\setcounter{secnumdepth}{3}

\captionsetup{justification=centering,font=small}

\lstset{language=Java, numbers=left, numberstyle=\tiny, numbersep=5pt}

\DeclareGraphicsExtensions{.pdf,.png,.jpg}

\pdfinfo {
  /Author   (Wilson Santos, 36288)
  /Title    (Implementação de um ResultSet Concorrente)
  /Subject  (Tese de Mestrado)
}


\begin{document}

% top matter

\title{Implementação de um ResultSet Concorrente}
\author{Wilson Santos, 36288 \texttt{$<$wilson.santos@ua.pt$>$}\\
  Universidade de Aveiro}
\maketitle

\begin{abstract}
A definição da API JDBC não prevê uma utilização concorrente dos serviços disponibilizados pelas várias interfaces que a constituem. Por isso, os drivers que implementam a API, não possuem suporte para um acesso concorrente. Neste documento são abordadas duas soluções para o acesso concorrente aos serviços disponibilizados pela interface \texttt{java.sql.ResultSet}. No final da implementação de ambas, são realizados testes de desempenho que avaliam a sua performance em relação à implementação standard. 
\end{abstract}

\tableofcontents
\addcontentsline{toc}{section}{Conteúdo}

\listoftables
\addcontentsline{toc}{section}{Lista de tabelas}

\listoffigures
\addcontentsline{toc}{section}{Lista de figuras}

\pagebreak

\section*{Introdução}
\addcontentsline{toc}{section}{Introdução}
Ambas as soluções que irão ser apresentadas nas secções seguintes assentam no mesmo princípio: providenciar um acesso concorrente fornecendo múltiplos cursores ao utilizador. A cada cursor está associado um contexto, isto é, a posição para a qual o cursor aponta no \emph{result set} partilhado. 

Uma das diferenças que distingue as duas soluções é a gestão da mudança de contexto. A mudança de contexto ocorre quando o cursor activo é alterado, sendo por isso necessário recuperar o contexto guardado desse cursor. Embora um thread possa possuir vários cursores, a situação mais usual para ocorrer a mudança de contexto é quando ocorre uma substituição do thread que se encontra activo.
\\

Neste documento quando se fala de implementação nativa do \emph{ResultSet}, refere-se à implementação realizada pelo driver JDBC da Microsoft (\href{http://www.microsoft.com/downloads/en/details.aspx?FamilyID=a737000d-68d0-4531-b65d-da0f2a735707&displaylang=en}{Microsoft SQL Server JDBC Driver 3.0}).

\newpage

\section{ResultSet Concorrente - 1ª Solução}
Esta solução tem como ponte de partida o desenho apresentado no paper "Assessment of a Enhanced ResultSet Component for Accessing Relational Databases" \cite{ERC10}. Ela é constituída por três elementos principais: a interface ICursor, a classe Cursor, a classe EResultSet. A classe EResultSet implementa ainda a interface ICursorFactory, que serve apenas para criar um novo cursor.

Esta solução será identificada como \emph{EResultSet}.

A interface ICursor tem uma assinatura semelhante à interface \texttt{java.sql.ResultSet}, pelo que fornece o mesmo nível de funcionalidade (mas neste momento apenas os serviços mais usuais estão implementados, por exemplo, existem apenas \emph{getters} para os tipos \emph{int}, \emph{String} e \emph{float}). A interface fornece ainda dois serviços adicionais que permitem bloquear desbloquear o acesso ao \emph{result set}.

\begin{figure}[H]
  \caption{Diagrama de classes do \emph{EResultSet}}
  \label{img:ers}
  \centering
  \includegraphics[width=15cm]{./diagrams/ers}
\end{figure}

\subsection{Mudança de contexto}
A mudança de contexto acontece quando o cursor que tem acesso ao \emph{result set} é alterado. Quando isso ocorre é necessário recuperar a linha para a qual o cursor apontava antes de ser alterado e mover o cursor nativo do \emph{result set} para essa linha. Esta gestão é realizada ao nível da classe EResultSet. Esta classe guarda em memória uma lista com o valor do número da linha para cada cursor. Quando é invocado um método que realiza uma operação sobre o \emph{result set}, é verificado se o cursor em execução foi alterado, e em caso afirmativo o cursor do \emph{result set} é movido para linha que foi lida da lista.

\subsection{Bloqueio do \emph{result set}}
\label{sec:sol1block}
Como foi mencionado anteriormente a interface de acesso ao \emph{result set} (ICursor) fornece a funcionalidade de bloquear o acesso ao \emph{result set}. Isto significa que é possível a um thread ter acesso exclusivo, podendo efectuar qualquer conjunto de operações sobre o \emph{result set}, sem existirem operações executadas por outros cursores. Quando o cursor já não necessitar de acesso exclusivo invoca a primitiva de desbloqueio.

\pagebreak

\section{ResultSet Concorrente - 2ª Solução}
Esta solução também assenta na ideia de fornecer vários cursores para o mesmo \emph{result set}\cite{ERC10}. Mas existem duas diferenças fundamentais: uma na gestão da mudança de contexto e outra no acesso aos serviços do \emph{result set}.

Esta solução será identificada como \emph{Cursor}.

\begin{figure}[H]
  \caption{Diagrama de classes do \emph{Cursor}}
  \label{img:cursor}
  \centering
  \includegraphics[width=14cm]{./diagrams/cursor}
\end{figure}

\subsection{Mudança de contexto}
A mudança de contexto em vez de ser gerida por um entidade central, passa a ser gerida pelos cursores. Cada cursor guarda a linha em que se encontra a trabalhar no \emph{result set}, assim quando esse cursor volta a ser o cursor activo lê directamente o contexto e recupera-o. Esta alteração faz com que esta solução seja muito mais escalável, pois já não é necessário definir um número pré-definido de cursores que irão ser criados. Pode ser criada livremente uma quantidade qualquer.

\subsection{Acesso aos serviços do \emph{result set}}
No \emph{EResultSet} todos os serviços do \emph{result set} são apresentados num interface único. O problema desta abordagem é que nem todos os serviços fazem sentido de existir, para o respectivo tipo de \emph{result set}. Por exemplo, não faz sentido existir um serviço que possibilita navegar para qualquer linha do \emph{result set}, quando o tipo de \emph{result set} só permite mover uma linha de cada vez e sempre no mesmo sentido. 

Daqui surgiu a ideia de criar uma interface para cada combinação tipo/concorrência do \emph{result set}. A Tabela \ref{tab:rstype-rsconcur} tem um sumário das possíveis combinações, e a partir da sua análise extraíram-se as interfaces e os métodos relevantes para cada uma delas.

\begin{table}[H]
  \caption{Combinações tipo / concorrência do \emph{result set}}
  \label{tab:rstype-rsconcur}
  \centering
\begin{tabular}{| l | l |}
  \hline
  Tipo & Concorrência \\ \hline
  TYPE\_FORWARD\_ONLY & CONCUR\_READ\_ONLY \\ \hline
  TYPE\_FORWARD\_ONLY & CONCUR\_UPDATABLE \\ \hline
  TYPE\_SCROLL\_INSENSITIVE & CONCUR\_READ\_ONLY \\ \hline
  TYPE\_SCROLL\_INSENSITIVE & CONCUR\_UPDATABLE \\ \hline
  TYPE\_SCROLL\_SENSITIVE & CONCUR\_READ\_ONLY \\ \hline
  TYPE\_SCROLL\_SENSITIVE & CONCUR\_UPDATABLE \\ \hline
  \end{tabular}
\end{table}

A Tabela \ref{tab:sol2iface} tem um resumo das interfaces existentes e das suas principais funcionalidades.

\begin{table}[H]
  \caption{Descrição das interfaces do \emph{Cursor}}
  \label{tab:sol2iface}
  \centering
\begin{tabular}{| l | p{7.3cm} |}
  \hline
  ICursorForwardReadOnly  & Permite percorrer o \emph{result set} num sentido, uma linha da cada vez e apenas fornece operações de leitura. \\ \hline
  ICursorForwardUpdate    & Permite percorrer o \emph{result set} num sentido, uma linha da cada vez e fornece operações de leitura e de escrita. \\ \hline
  ICursorScrollReadOnly   & Permite percorrer o \emph{result set} em qualquer sentido, múltiplas linhas da cada vez e apenas fornece operações de leitura. \\ \hline
  ICursorScrollUpdate     & Permite percorrer o \emph{result set} em qualquer sentido, múltiplas linhas da cada vez e fornece operações de leitura e de escrita. \\ \hline
\end{tabular}
\end{table}

O trabalho apresentado no \emph{paper} \cite{CRUDDOM10} é todo em volta da procura de uma solução para o problema de estabelecer uma ponte entre o paradigma \emph{Orientado-aos-Objectos} e o paradigma \emph{Relacional}, garantindo um interface de elevada usabilidade que fornece apenas os serviços necessários, e de certa forma motivou para a procura de uma forma de integrar essa ideia num \emph{result set} concorrente.

\subsection{Bloqueio do \emph{result set}}
O bloqueio funciona em tudo de igual modo à primeira solução. Ver secção \ref{sec:sol1block}.

\newpage

\section{Medição do desempenho}
Nesta secção é descrito o modo como foi medido o desempenho das soluções implementadas.
\\

Para avaliar o desempenho foi medido o tempo que uma quantidade de threads leva a completar um conjunto de tarefas. Existem três tipos de conjuntos de tarefas: 
\begin{itemize}
  \item \textbf{leitura}, usando os métodos \texttt{getXXX()} da interface do \emph{result set}.
  \item \textbf{actualização}, usando os métodos \texttt{updateXXX()} e \texttt{updateRow()} do Result Set.
  \item \textbf{inserção}, usando os métodos \texttt{updateXXX()} e \texttt{insertRow()} do Result Set.
\end{itemize}

Os três tipos de tarefas foram executados sobre as três implementações da interface do Result Set: standard, \emph{EResultSet} (1ª Solução) e \emph{Cursor} (2ª Solução). Assim para todas as combinações tipo de tarefa / implementação, foi criado um \texttt{runnable} que seria executado por um thread. 

A Tabela \ref{tab:sol2runnable} contém uma pequena descrição de cada tipo de \texttt{runnable} criado.

\begin{table}[H]
  \caption{Resumo dos \texttt{runnable} utilizados nas medições do desempenho}
  \label{tab:sol2runnable}
  \centering
\begin{tabular}{| l | p{7.3cm} |}
  \hline
  ResultSetRead     & runnable que utiliza a implementação standard para efectuar leituras no \emph{result set}. \\ \hline
  ResultSetUpdate   & runnable que utiliza a implementação standard para efectuar actualizações no \emph{result set}. \\ \hline
  ResultSetInsert   & runnable que utiliza a implementação standard para efectuar inserções no \emph{result set}. \\ \hline
  EResultSetRead    & runnable que utiliza a 1ª solução para efectuar leituras no \emph{result set}. \\ \hline
  EResultSetUpdate  & runnable que utiliza a 1ª solução para efectuar actualizações no \emph{result set}. \\ \hline
  EResultSetInsert  & runnable que utiliza a 1ª solução para efectuar inserções no \emph{result set}. \\ \hline
  CursorRead        & runnable que utiliza a 2ª solução para efectuar leituras no \emph{result set}. \\ \hline
  CursorUpdate      & runnable que utiliza a 2ª solução para efectuar actualizações no \emph{result set}. \\ \hline
  CursorInsert      & runnable que utiliza a 2ª solução para efectuar inserções no \emph{result set}. \\ \hline
\end{tabular}
\end{table}

Os tempos medidos (ver Imagens \ref{img:Read}, \ref{img:Update} e \ref{img:Insert}) correspondem à soma de dois valores: o tempo total que um número de threads demora a executar as tarefas indicadas pelo \texttt{runnable} (descrito na secção \ref{ref:executiontime}), e o tempo que é necessário para preparar o ambiente de execução dos threads (descrito na secção \ref{ref:setuptime}).

\subsection{Configuração dos testes}
As \emph{statements} usadas para criar os objectos do tipo ResultSet foram configuradas da seguinte forma:
\begin{itemize}
  \item tipo, \texttt{ResultSet.TYPE\_SCROLLABLE\_SENSITIVE}.
  \item concorrência, \texttt{ResultSet.CONCUR\_UPDATABLE}.
\end{itemize}

Os threads trabalharam sobre a tabela da Imagem \ref{img:testtable}. Essa tabela é limpa antes de todas as medições.

\begin{figure}[H]
  \caption{Estrutura da tabela de testes}
  \label{img:testtable}
  \centering
  \includegraphics[width=4cm]{./diagrams/dbTestData}
\end{figure}

\subsection{Tempo de preparação}
\label{ref:setuptime}

O tempo de preparação (setup) consiste no tempo que o código que prepara a execução dos threads demora a ser executado. Para todas as tarefas a contagem do tempo de preparação foi realizada da seguinte forma:

\begin{figure}[H]
  \caption{Contagem do tempo de preparação}
  \label{img:countSetup}
  \centering
  \includegraphics[width=8cm]{./diagrams/countSetup}
\end{figure}

~\\
Quando se usa o \emph{\textbf{ResultSet}} (implementação \emph{standard}) não é possível partilhar o objecto do \emph{result set} entre os threads. Por isso é necessário executar uma \emph{query} e obter um \emph{result set} para cada thread.

\lstinputlisting[firstline=176,lastline=181,caption=Código de preparação para o ResultSet.,label=lst:setuprs,frame=tb]{../../src/ws/jdbc/concurrent/test/ResultSetConcurrentTest.java}

A classe \emph{ThreadManager} quando instanciada cria internamente uma lista de threads com tamanho igual a \texttt{numberOfThreads}, e a cada thread associa uma nova instância do \texttt{runnable} que é passado como segundo argumento para o construtor. O método \texttt{addLog} regista o tempo que o código demorou a ser executado.
\\

Cada \texttt{runnable} do \emph{ResultSet} no seu construtor cria um \emph{result set} só para si, tal como demonstrado na Listagem \ref{lst:rsconstructor}.

\lstinputlisting[firstline=23,lastline=26,caption=Construtor de um \texttt{runnable} do ResultSet.,label=lst:rsconstructor,frame=tb]{../../src/ws/jdbc/concurrent/test/tasks/ResultSetRunnable.java}

~\\
No caso do \emph{\textbf{EResultSet}} o objecto do \emph{result set} pode ser partilhado, pelo que só é necessário executar uma \emph{query} e obter um \emph{result set} (linha 6 da Listagem \ref{lst:setupers}), que depois é usado para criar um objecto da classe EResultSet, que por sua vez é usado para criar os cursores.

\lstinputlisting[firstline=145,lastline=156,caption=Código de preparação para o \emph{EResultSet}.,label=lst:setupers,frame=tb]{../../src/ws/jdbc/concurrent/test/ResultSetConcurrentTest.java}

~\\
Também no \emph{\textbf{Cursor}} o objecto do \emph{result set} pode ser partilhado (linha 6 da Listagem \ref{lst:setupcursor}), e quando é criado através da execução da \emph{query} é passado a uma fábrica de cursores. Essa fábrica depois cria um cursor para cada thread.

\lstinputlisting[firstline=161,lastline=171,caption=Código de preparação para o Cursor.,label=lst:setupcursor,frame=tb]{../../src/ws/jdbc/concurrent/test/ResultSetConcurrentTest.java}

\subsection{Tempo de execução}
\label{ref:executiontime}

O tempo de execução corresponde ao tempo total que um número de threads demora a terminar a tarefa que lhes foi dada. Para todas as tarefas a contagem do tempo de execução foi realizada da seguinte forma:

\begin{figure}[H]
  \caption{Contagem do tempo de execução}
  \label{img:countExecution}
  \centering
  \includegraphics[width=8cm]{./diagrams/countExecution}
\end{figure}

Como foi referido anteriormente existem três categorias de tarefas a serem executadas pelos threads: leitura, actualização e inserção. A seguir cada uma delas é descrita.

\subsubsection{Leitura}
As leituras foram realizadas nas seguintes condições:
\begin{itemize}
  \item Foram seleccionadas todas as colunas da tabela (\texttt{"SELECT * FROM TestData"}).
  \item A tabela \emph{"TestData"} foi preenchida com 20 linhas.
  \item Foram lidas sequencialmente todas as linhas da tabela.
  \item Utilizaram-se os \emph{getters} que indexam as colunas com um inteiro.
\end{itemize}

~\\
O código para realizar as leituras encontra-se na Listagem \ref{lst:read}.

\lstinputlisting[firstline=19,lastline=24,caption=Código de leitura.,label=lst:read,frame=tb]{../../src/ws/jdbc/concurrent/test/tasks/read/ResultSetRead.java}

\subsubsection{Actualização}
As actualizações foram realizadas nas seguintes condições:
\begin{itemize}
  \item Foram seleccionadas todas as colunas da tabela (\texttt{"SELECT * FROM TestData"}).
  \item A tabela \emph{"TestData"} foi preenchida com 20 linhas.
  \item Foram actualizadas sequencialmente todas as linhas da tabela.
  \item Utilizaram-se os \emph{getters} e \emph{setters} que indexam as colunas com um inteiro.
\end{itemize}

~\\
O código para realizar as actualizações encontra-se na Listagem \ref{lst:update}.

\lstinputlisting[firstline=19,lastline=25,caption=Código de actualização.,label=lst:update,frame=tb]{../../src/ws/jdbc/concurrent/test/tasks/update/ResultSetUpdate.java}

\subsubsection{Inserção}
As inserções foram realizadas nas seguintes condições:
\begin{itemize}
  \item Foram seleccionadas todas as colunas da tabela (\texttt{"SELECT * FROM TestData"}).
  \item A tabela \emph{"TestData"} não continha dados.
  \item Foram inseridas sequencialmente 20 linhas na tabela.
  \item Utilizaram-se métodos que indexam as colunas com um inteiro.
\end{itemize}

~\\
O código para realizar as inserções encontra-se na Listagem \ref{lst:insert}.

\lstinputlisting[firstline=22,lastline=29,caption=Código de inserção.,label=lst:insert,frame=tb]{../../src/ws/jdbc/concurrent/test/tasks/insert/ResultSetInsert.java}

~\\
A variável \texttt{insertCount} contém o número de linhas que serão inseridas.

\newpage

\section{Resultados}
O teste de desempenho foi realizado num cenário cliente-servidor. Do lado do cliente correram os threads e do lado do servidor estava a base de dados, que atendia aos pedidos dos threads.

O cliente é numa máquina com o processador Intel SU4100 @1.3GHz, com 4.0GB de memória, sistema operativo Linux 2.6.32. O servidor estava a correr numa máquina virtual com 1.2GB de memória e sistema operativo Windows XP SP3. A comunicação entre os dois foi estabelecida usando uma interface \texttt{tap} no cliente e uma interface \emph{Bridged} no servidor.

As tabelas a seguir contêm as medições realizadas e refletem a média dos 10 melhores resultados, seleccionados de um conjunto de 25 amostras.

Os valores das três tabelas seguintes apresentam os valores totais, isto é, tempo de preparação mais tempo de execução. Os valores de todas as tabelas encontram-se em \emph{milissegundos}.

\begin{figure}[H]
  \caption{Resultados das medições de desempenho - Leitura}
  \label{img:Read}
  \centering
  \includegraphics[width=10cm]{./results/imgs/Read}
\end{figure}

\begin{figure}[H]
  \caption{Resultados das medições de desempenho - Actualização}
  \label{img:Update}
  \centering
  \includegraphics[width=10cm]{./results/imgs/Update}
\end{figure}

\begin{figure}[H]
  \caption{Resultados das medições de desempenho - Inserção}
  \label{img:Insert}
  \centering
  \includegraphics[width=10cm]{./results/imgs/Insert}
\end{figure}

Os valores das medidas dos tempos de preparação ajudam a encontrar uma das razões que levou os resultados das duas soluções serem melhores aos do \emph{ResultSet} standard. Como podemos ver na tabela da Image \ref{img:Select}, enquanto que para o \emph{Cursor} e \emph{EResultSet} o tempo aumenta muito ligeiramente quando são criados mais threads, no ResultSet o tempo de preparação é proporcional ao número de threads criados. Daqui podemos concluir que um dos trunfos das duas soluções em relação ao \emph{ResultSet} é o facto de ser apenas necessário realizar uma \emph{query}, cujo resultado pode ser partilhado pelos threads.

\begin{figure}[H]
  \caption{Resultados das medições de desempenho - Preparação}
  \label{img:Select}
  \centering
  \includegraphics[width=10cm]{./results/imgs/Select}
\end{figure}

O gráfico da Imagem \ref{img:Select} salienta o que já foi referido. A diferença entre tempos de preparação é tão grande que os tempos de preparação do \emph{Cursor} e do \emph{EResultSet} parecem zero, e por isso estão "colados" ao eixo do \emph{x}.

\subsection{Comparação}
Nesta secção serão efectuadas comparações entre os resultados medidos, de modo a avaliar as implementações na execução das diferentes tarefas.
\\

Primeiro é analisado o peso que a preparação e a execução têm sobre o resultado final. A seguir é feita a comparação entre as duas implementações e o ResultSet standard.

\subsubsection{Peso da preparação \emph{versus} execução}
\label{sec:pe}
Nessa secção iremos analisar o peso do tempo de preparação \emph{versus} o tempo de execução.
\\

O primeiro aspecto a salientar é que para as duas soluções implementadas, o peso da preparação é pequeno e nunca ultrapassa os 15\% (e muitas vezes é menor que 5\%). Já no caso do \emph{ResultSet} vemos que é na preparação que se gasta muita parte do tempo. Na leitura (Imagem \ref{img:peL}) e para 50 threads, a preparação chega a ultrapassar um peso de 70\%. Apenas quando se trata de uma operação mais demorada (actualização e inserção) o peso ronda os 15\%-20\%.

Um outro aspecto evidenciado nos gráficos é que para o \emph{Cursor} e o \emph{EResultSet} o tempo de preparação tem menos peso à medida que o número de threads aumenta, enquanto que para \emph{ResultSet} o peso mantém-se relativamente constante.

O tempo de preparação para o \emph{Cursor} e do \emph{EResultSet} é semelhante. Apenas na actualização (Imagem \ref{img:peA}) para 10 threads o \emph{EResultSet} não ultrapassa os 2\%, e o \emph{Cursor} chega aos 15\%. % TODO explicar a razão

\begin{figure}[H]
  \caption{Preparação \emph{versus} Execução - \textbf{Leitura}.}
  \label{img:peL}
  \centering
  {\small Legenda: E - execução, P - preparação, T - total}\\
  \includegraphics[width=7.5cm]{./results/imgs/peLE}
  \includegraphics[width=7.5cm]{./results/imgs/peLC}
  \includegraphics[width=7.5cm]{./results/imgs/peLR}
\end{figure}

\begin{figure}[H]
  \caption{Preparação \emph{versus} Execução - \textbf{Actualização}.}
  \label{img:peA}
  \centering
  {\small Legenda: E - execução, P - preparação, T - total}\\
  \includegraphics[width=7.5cm]{./results/imgs/peAE}
  \includegraphics[width=7.5cm]{./results/imgs/peAC}
  \includegraphics[width=7.5cm]{./results/imgs/peAR}
\end{figure}

\begin{figure}[H]
  \caption{Preparação \emph{versus} Execução - \textbf{Inserção}.}
  \label{img:peI}
  \centering
  {\small Legenda: E - execução, P - preparação, T - total}\\
  \includegraphics[width=7.5cm]{./results/imgs/peIE}
  \includegraphics[width=7.5cm]{./results/imgs/peIC}
  \includegraphics[width=7.5cm]{./results/imgs/peIR}
\end{figure}

\subsubsection{Implementações \emph{versus} ResultSet}
Nesta secção é realizada uma comparação entre as duas soluções e o \emph{ResultSet}. Para isso efetuou-se o quociente entre os valores medidos para o \emph{ResultSet} e os valores medidos para ambas as soluções. Os valores acima de '\textbf{1}' indicam que as soluções implementadas apresentaram um desempenho superior à implementação \emph{standard}.
\\

Primeiro vamos analisar os resultados obtidos apenas para o tempo de execução. Tirando a operação de leitura, ambas as implementações apresentaram um desempenho superior à implementação standard, embora sejam, na sua maioria, apenas marginais.

\begin{figure}[H]
  \caption{Comparação entre \emph{ResultSet} e as duas soluções - tempo de execução}
  \label{img:compExec}
  \centering
  \includegraphics[width=12cm]{./results/imgs/compExec}
\end{figure}

Como já tínhamos visto na Secção \ref{sec:pe} o tempo de preparação não deve ser desprezado, e na operação de leitura tem um peso enorme para o \emph{ResultSet}. Essa é a razão para os resultados terem disparado, fazendo com que as duas implementações tenham um desempenho pelo menos \textbf{duas vez maior} (Imagem \ref{img:compTotal}). 

Relativamente às outras duas operações (actualização e inserção) também se registam ganhos, porém mais moderados. Enquanto que no gráfico \ref{img:compExec} os ganhos quase nunca ultrapassaram os 10\%, agora temos como ganhos mínimos 18\% e ganhos máximos 38\%, aproximadamente.

\begin{figure}[H]
  \caption{Comparação entre \emph{ResultSet} e as duas soluções - tempo total}
  \label{img:compTotal}
  \centering
  \includegraphics[width=12cm]{./results/imgs/compTotal}
\end{figure}

Comparando os ganhos das duas implementações não existe um claro vencedor. Os valores dos ganhos foram sempre muito próximos, no entanto, nos 15 testes do gráfico \ref{img:compExec} o \emph{Cursor} apresentou melhor desempenho em 11, e nos 15 testes do gráfico \ref{img:compTotal} ganhou em 10 (curiosamente houve um empate). Por isso, se tiver de ser eleito um vencedor será o \emph{Cursor}, porque para além de obter mais ganhos (mesmo que sejam mínimos), tem também a vantagem de ser mais escalável e apresentar uma interface mais orientada à usabilidade.

\pagebreak

\bibliography{mybib}{}
\addcontentsline{toc}{section}{Bibliografia}
\bibliographystyle{unsrt}

\end{document}
